package test.threads.queue.unstressed.impl;

import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

import test.threads.queue.unstressed.IBoundedQueueFactory;
import test.threads.queue.unstressed.IQueue;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Упрощенный вариант, без наследования -- чтобы постить в блог
 *
 * @author cheremin
 * @since 27.09.11,  14:58
 */
public class CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue<T> implements IQueue<T> {
    private static final boolean USE_BITMASK = true;
    //======================================================
    private static final AtomicLongFieldUpdater<CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue> tailUpdater = AtomicLongFieldUpdater.newUpdater( CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue.class, "tailCursor" );
    private static final AtomicLongFieldUpdater<CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue> headUpdater = AtomicLongFieldUpdater.newUpdater( CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue.class, "headCursor" );
    //======================================================

    /* Elements range: [headCursor, tailCursor)
    *
    * (tailCursor - headCursor) == elements count
    *
    * 0 <= (tailCursor - headCursor) <= length  => state invariant
    *
    * tailCursor - headCursor == length         => queue is full
    * tailCursor - headCursor == 0              => queue is empty
    *
    * (headCursor % size ) is the index of first item in queue
    * (tailCursor % size ) is the index of _cell_ for _next last item_
    */

    private final int length;

    public volatile long $pad11, $pad12, $pad13, $pad14, $pad15, $pad16, $pad17, $pad18 = 7;
    private volatile long headCursor = 0;
    public volatile long $pad21, $pad22, $pad23, $pad24, $pad25, $pad26, $pad27, $pad28 = 7;
    private volatile long tailCursor = 0;
    public volatile long $pad31, $pad32, $pad33, $pad34, $pad35, $pad36, $pad37, $pad38 = 8;

    private long lastHeadObserved = 0;
    public volatile long $pad41, $pad42, $pad43, $pad44, $pad45, $pad46, $pad47, $pad48 = 9;
    private long lastTailObserved = 0;
    public volatile long $pad51, $pad52, $pad53, $pad54, $pad55, $pad56, $pad57, $pad58 = 9;


    private final T[] elements;

    public long sumPaddingToPreventOptimisation() {
        try {
            long sum = 0;
            for ( final Field field : getClass().getFields() ) {
                if ( field.getName().startsWith( "$pad" ) && field.getType() == long.class ) {
                    final Number value = ( Number ) field.get( this );
                    sum += value.longValue();
                }
            }
            return sum;
        } catch ( IllegalAccessException e ) {
            return 0;
        }
    }


    public CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue( final int length ) {
        checkArgument( length > 0, "length(%s) must be >0", length );
        if ( USE_BITMASK ) {
            checkArgument( Integer.bitCount( length ) == 1, "length(%s) must be a power of 2", length );
        }
        this.length = length;
        elements = ( T[] ) new Object[length];
        sumPaddingToPreventOptimisation();
    }

    private long nextTail() {
        final long tail = tailCursor;
        waitWhileNotFull( tail );

        assert ( tail == tailCursor ) : "tail=" + tail + " <> tailCursor=" + tailCursor + " -- seems like multi-threaded enqueue() detected!";
        return tail + 1;
    }

    private long nextHead() {
        final long head = headCursor;
        waitWhileNotEmpty( head );
        assert ( head == headCursor ) : "head=" + head + " <> headCursor=" + headCursor + " -- seems like multi-threaded dequeue() detected!";
        return head + 1;
    }


    private void publishTail( final long newTail ) {
        assert ( newTail >= headCursor ) : "newTail(" + newTail + ") < headCursor(" + headCursor + ")";
        tailUpdater.lazySet( this, newTail );
    }

    private void publishHead( final long newHead ) {
        assert ( newHead <= tailCursor ) : "newHead(" + newHead + ") > tailCursor(" + tailCursor + ")";
        headUpdater.lazySet( this, newHead );
    }


    private void waitWhileNotFull( final long tail ) {
        //spin-wait: "while not full"
        final long target = tail - length;
        while ( target == lastHeadObserved ) {
            lastHeadObserved = headCursor;
        }
    }

    private void waitWhileNotEmpty( final long head ) {
        //spin-wait: "while not empty"
        while ( head == lastTailObserved ) {
            lastTailObserved = tailCursor;
        }
    }

    public void enqueue( final T item ) {
        final long newTail = nextTail();

        final int index = index( newTail );
        elements[index] = item;

        publishTail( newTail );
    }

    public T dequeue() {
        final long newHead = nextHead();

        final int index = index( newHead );
        final T item = elements[index];
        //elements[index] = null;

        publishHead( newHead );
        return item;
    }

    private int index( final long sequence ) {
        if ( USE_BITMASK ) {
            return ( int ) ( ( sequence - 1 ) & ( length - 1 ) );
        } else {
            return ( int ) ( ( sequence - 1 ) % length );
        }
    }

    public int size() {
        return ( int ) ( tailCursor - headCursor );
    }

    @SuppressWarnings( "unchecked" )
    public static <T> IBoundedQueueFactory<T, CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue<T>> factory() {
        return FACTORY;
    }

    public static final IBoundedQueueFactory FACTORY = new IBoundedQueueFactory<Object, CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue<Object>>() {
        @Override
        public CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue<Object> create( final int size ) {
            return new CABSESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue<Object>( size );
        }

        @Override
        public String toString() {
            return "SESDLazySetPaddedSpinOptimizedCleanFreeUnrolledQueue[" + ( USE_BITMASK ? "&" : "%" ) + "]";
        }
    };
}
